// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build windows 

// 软件包注册表提供对windows注册表的访问。这里是一个简单的例子，打开一个注册表项并从中读取一个字符串值。
// 
// k，err:=注册表。OpenKey（registry.LOCAL_MACHINE，`SOFTWARE\Microsoft\Windows NT\CurrentVersion`，registry.QUERY_VALUE）
// if err！=无{
// log.Fatal（err）
// }
// defer k.Close（）
// 
// s，，err:=k.GetStringValue（“系统根”）
// if err无{
// log.致命（错误）
// }
// fmt。Printf（“Windows系统根目录是%q\n”，s）
// 
// 注意：此软件包是golang的副本。org/x/sys/windows/registry 
// with KeyInfo。移除ModTime以防止依赖循环。
// 
package registry

import (
	"runtime"
	"syscall"
)

const (
	// 注册表项安全和访问权限。
	// 有关详细信息，请参阅https:
	// 。
	ALL_ACCESS         = 0xf003f
	CREATE_LINK        = 0x00020
	CREATE_SUB_KEY     = 0x00004
	ENUMERATE_SUB_KEYS = 0x00008
	EXECUTE            = 0x20019
	NOTIFY             = 0x00010
	QUERY_VALUE        = 0x00001
	READ               = 0x20019
	SET_VALUE          = 0x00002
	WOW64_32KEY        = 0x00200
	WOW64_64KEY        = 0x00100
	WRITE              = 0x20006
)

// 项是打开的Windows注册表项的句柄。
// 通过调用OpenKey可以获取密钥；有
// 还有一些预定义的根键，例如当前用户。
// 密钥可以直接在Windows API中使用。
type Key syscall.Handle

const (
	// Windows定义了一些始终打开的预定义根键。
	// 应用程序可以使用这些键作为注册表的入口点。
	// 通常这些密钥在OpenKey中用于打开新密钥，
	// 但它们也可以在需要密钥的任何地方使用。
	CLASSES_ROOT   = Key(syscall.HKEY_CLASSES_ROOT)
	CURRENT_USER   = Key(syscall.HKEY_CURRENT_USER)
	LOCAL_MACHINE  = Key(syscall.HKEY_LOCAL_MACHINE)
	USERS          = Key(syscall.HKEY_USERS)
	CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG)
)

// Close Close open key k.
func (k Key) Close() error {
	return syscall.RegCloseKey(syscall.Handle(k))
}

// OpenKey打开一个路径名与key k相对的新key.
// 它接受任何打开的key，包括当前用户和其他用户，
// 并返回新的key和一个错误。
// access参数指定要打开的
// 密钥的所需访问权限。
func OpenKey(k Key, path string, access uint32) (Key, error) {
	p, err := syscall.UTF16PtrFromString(path)
	if err != nil {
		return 0, err
	}
	var subkey syscall.Handle
	err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey)
	if err != nil {
		return 0, err
	}
	return Key(subkey), nil
}

// ReadSubKeyNames返回键k的子键的名称。
func (k Key) ReadSubKeyNames() ([]string, error) {
	// RegEnumKeyEx必须重复调用并完成。
	// 在此期间，此goroutine无法从
	// 其当前线程迁移出去。见#49320。
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	names := make([]string, 0)
	// 注册表项大小限制为255字节，如图所示：
	// https:
	buf := make([]uint16, 256) // 加上终止零字节的额外空间
loopItems:
	for i := uint32(0); ; i++ {
		l := uint32(len(buf))
		for {
			err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
			if err == nil {
				break
			}
			if err == syscall.ERROR_MORE_DATA {
				// 将缓冲区大小加倍，然后重试。
				l = uint32(2 * len(buf))
				buf = make([]uint16, l)
				continue
			}
			if err == _ERROR_NO_MORE_ITEMS {
				break loopItems
			}
			return names, err
		}
		names = append(names, syscall.UTF16ToString(buf[:l]))
	}
	return names, nil
}

// CreateKey在开放密钥k下创建一个名为path的密钥。
// CreateKey返回新密钥和一个布尔标志，该标志报告
// 密钥是否已经存在。
// access参数指定要创建的密钥
// 的访问权限。
func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) {
	var h syscall.Handle
	var d uint32
	err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path),
		0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d)
	if err != nil {
		return 0, false, err
	}
	return Key(h), d == _REG_OPENED_EXISTING_KEY, nil
}

// DeleteKey删除键k的子键路径及其值。
func DeleteKey(k Key, path string) error {
	return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path))
}

// 密钥信息描述密钥的统计信息。它由Stat.
type KeyInfo struct {
	SubKeyCount     uint32
	MaxSubKeyLen    uint32 // 返回，以Unicode字符表示，不包括终止的零字节
	ValueCount      uint32
	MaxValueNameLen uint32 // 键的最长值名称的大小，以Unicode字符表示，不包括终止的零字节
	MaxValueLen     uint32 // 键值中最长的数据组件，字节
	lastWriteTime   syscall.Filetime
}

// Stat检索关于开放密钥k的信息。
func (k Key) Stat() (*KeyInfo, error) {
	var ki KeyInfo
	err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil,
		&ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount,
		&ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime)
	if err != nil {
		return nil, err
	}
	return &ki, nil
}
